﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;


namespace Nirvana
{
    /// <summary>
    /// toggle控制伸缩、展开组件，需要Toggle Activator脚本配合隐藏，显示UI icon
    /// </summary>
    [RequireComponent(typeof(RectTransform))]
    [AddComponentMenu("Nirvana/UI/Accordion Element")]
    public sealed class AccordionElement : Toggle
    {
        /// <summary>
        /// 需要展开，缩放的对象
        /// </summary>
        [SerializeField]
        [Tooltip("The min height of this element.")]
        private LayoutElement content;
        private HorizontalOrVerticalLayoutGroup layoutGroup;

        private Accordion accordion;
        private RectTransform rectTransform;
        private ScrollRect sv;
        private RectTransform svRectTransform;

        private float spacing;
        private float preferredHeight;
        private float startSpacing;
        private float endSpacing;
        
        private float duration = -1f;
        private int framecount;
        private float curPreferredHeight = -1f;
        private float verticalNormalizedPosition = -1f;

        private bool isActive;

        protected override void Awake()
        {
            base.Awake();
            this.layoutGroup = this.content.GetComponent<HorizontalOrVerticalLayoutGroup>();
            this.spacing = this.layoutGroup.spacing;
            base.transition = 0;
            this.toggleTransition = 0;
            this.accordion = GetComponentInParent<Accordion>();
            this.rectTransform = (transform as RectTransform);
            
            this.sv = GetComponentInParent<ScrollRect>();
            if (this.sv != null) this.svRectTransform = this.sv.GetComponent<RectTransform>();

            this.onValueChanged.AddListener(OnValueChange);
            OnValueChange(isOn);
        }

#if UNITY_EDITOR
        protected override void OnValidate()
        {
            base.OnValidate();
            if(this.content != null)
            {
                if (isOn) this.content.preferredHeight = -1f;
                else this.content.preferredHeight = 0f;
            }

        }
#endif

        public void Refresh()
        {
            OnValueChange(isOn);
        }

        private void OnValueChange(bool isActive)
        {
            if (this.content == null) return;
            Accordion.Transition trasition = this.accordion == null ? Accordion.Transition.Instant : this.accordion.TransitionType;
            if(trasition == Accordion.Transition.Instant)
            {
                if (isActive)
                {
                    this.content.gameObject.SetActive(true);
                    this.content.preferredHeight = -1f;
                }
                else
                {
                    this.content.gameObject.SetActive(false);
                    this.content.preferredHeight = 0f;
                }
            }else if(trasition == Accordion.Transition.Tween)
            {
                this.duration = this.accordion.TransitionDuration;
                this.framecount = Time.frameCount;
                this.curPreferredHeight = -1f;
                if (isOn)
                {
                    this.content.gameObject.SetActive(true);
                }
            }

        }


        private float GetPreferredHeight()
        {
            if (this.content == null) return 0f;
            float height = this.content.preferredHeight;
            this.content.preferredHeight = -1f;
            var rect = this.content.GetComponent<RectTransform>();
            var newHeight= LayoutUtility.GetPreferredHeight(rect);
            this.content.preferredHeight = height;
            return newHeight;
        }

        private void SetPreferredHeight(float height)
        {
            if (this.content == null) return;
            this.content.preferredHeight = height;
        }

        private void Update()
        {
            if (!Application.isPlaying) return;
            if(this.verticalNormalizedPosition >= 0f)
            {
                float dist = this.verticalNormalizedPosition - this.sv.verticalNormalizedPosition;
                float speed = 2.5f * Time.deltaTime;
                if(Mathf.Abs(dist)<= speed)
                {
                    this.sv.verticalNormalizedPosition = this.verticalNormalizedPosition;
                    this.verticalNormalizedPosition = -1f;
                }
                else
                {
                    this.sv.verticalNormalizedPosition += Mathf.Sign(dist) * speed;
                }

            }

            if(this.duration >= 0f)
            {
                if(this.curPreferredHeight < 0f && Time.frameCount > this.framecount)
                {
                    if (isOn)
                    {
                        this.curPreferredHeight = 0f;
                        this.preferredHeight = GetPreferredHeight();
                        this.startSpacing = 0f;
                        this.endSpacing = this.spacing;
                        this.isActive = true;
                    }
                    else
                    {
                        this.curPreferredHeight = GetPreferredHeight();
                        this.preferredHeight = 0;
                        this.startSpacing = this.spacing;
                        this.endSpacing = 0;
                        this.isActive = false;
                    }
                }

                if(this.curPreferredHeight >= 0f)
                {
                    float d = this.duration / this.accordion.TransitionDuration;
                    float h = Mathf.Lerp(this.preferredHeight, this.curPreferredHeight, d);
                    this.SetPreferredHeight(h);
                    float s = Mathf.Lerp(this.endSpacing, this.startSpacing, d);
                    this.layoutGroup.spacing = spacing;
                    this.duration -= Time.deltaTime;
                    if(this.duration <= 0f)
                    {
                        this.SetPreferredHeight(this.preferredHeight);
                        this.content.gameObject.SetActive(this.isActive);
                        this.duration = -1f;
                    }

                    if(this.isActive && this.sv != null)
                    {
                        UpdateSV(this.sv, this.svRectTransform, this.rectTransform);
                    }

                }
            }
        }

        private void UpdateSV(ScrollRect sr, RectTransform rect1, RectTransform rect2)
        {
            float space1 = 0;
            float space2 = 0;
            float space3 = 0;
            RectTransform _rectTransform = sr.content;
            Transform _transform = _rectTransform.transform;
            HorizontalOrVerticalLayoutGroup _layoutgroup = _rectTransform.GetComponent<HorizontalOrVerticalLayoutGroup>();
            for(int i = 0, imax = _transform.childCount; i < imax; i++)
            {
                Transform child = _transform.GetChild(i);
                if (child.gameObject.activeInHierarchy)
                {
                    if (child == transform) space2 = space1;
                    var _rect = child.GetComponent<RectTransform>();
                    space1 += LayoutUtility.GetPreferredHeight(_rect);
                    if (_layoutgroup != null && i != imax - 1) space1 += _layoutgroup.spacing;
                    if (child == _transform) space3 = space1;

                }
            }

            float height = sr.viewport.rect.height;
            float space4 = space1 - height;
            if(space4 > 0f)
            {
                float space5 = (1f - sr.verticalNormalizedPosition) * space4;
                float space6 = space5 + height;
                if(space2 < space5 || space3 > space6)
                {
                    if(space3 - space2 < height)
                    {
                        float space7 = space5 + space3 - space6;
                        float space8 = 1f - space7 / space4;
                        this.verticalNormalizedPosition = Mathf.Clamp(space8, 0f, 1f);
                    }
                    else
                    {
                        float space9 = 1f - space2 / space4;
                        this.verticalNormalizedPosition = Mathf.Clamp(space9, 0f, 1f);
                    }

                }

            }


        }



    }
}

